/*******************************************************************************
 * Copyleft (c) 2021 Kcode
 *
 * @file    ctr.c
 * @brief   支持在电脑显示屏(VGA)上显示的设备文件，需要vga库编译
 * @author  K
 * @version 0.0.1
 * @date    2021-07-02
 * @license MulanPSL-1.0
 *
 * 文件修改历史：
 * <时间>       | <版本>    | <作者>  | <描述>
 * 2021-07-02   | v0.0.1    | Kcode   | 支持在电脑显示屏(VGA)上显示的设备文件
 * -----------------------------------------------------------------------------
 ******************************************************************************/

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <string.h>
#include <vga.h>
#include <vgagl.h>
#include <stdlib.h>

#include "config.h"
#include "disp_manager.h"
#include "debug_manager.h"

static GraphicsContext *s_physicalscreen;
static GraphicsContext *s_virtualscreen;

static int CRTDeviceInit(void);
static int CRTDeviceExit(void);
static int CRTShowPixel(int x, int y, unsigned int color);
static int CRTCleanScreen(unsigned int backcolor);
static int CRTShowPage(PT_PIXELDATAS ptVideoMem);

static T_DispOpr s_tCRTOpr = {
	.name        = "crt",
	.DeviceInit  = CRTDeviceInit,
	.DeviceExit  = CRTDeviceExit,
	.ShowPixel   = CRTShowPixel,
	.CleanScreen = CRTCleanScreen,
	.ShowPage    = CRTShowPage,
};

/*!
 * @brief  "CRTDevice显示设备"的初始化函数
 * @param  [in] 无
 * @return 0：注册成功，-1：失败
 */
 static int CRTDeviceInit(void)
{
    /* 初始化 */
    vga_init();

    /* 设置工作模式 */
    vga_setmode(G640x480x64K);
    
    gl_setcontextvga(G640x480x64K);

    /* 获得"物理屏幕" */
    s_physicalscreen = gl_allocatecontext();
    gl_getcontext(s_physicalscreen);

    /* 获得"虚拟屏幕" */
    gl_setcontextvgavirtual(G640x480x64K);
    s_virtualscreen = gl_allocatecontext();
    gl_getcontext(s_virtualscreen);

    /* 设置"虚拟屏幕"为当前所使用的"屏幕" */
    gl_setcontext(s_virtualscreen);
    
    s_tCRTOpr.Xres = 640;
    s_tCRTOpr.Yres = 480;
    s_tCRTOpr.Bpp  = 32; 

    s_tCRTOpr.LineWidth = s_tCRTOpr.Xres * s_tCRTOpr.Bpp / 8;

    s_tCRTOpr.pDispMem = (unsigned char *)malloc(s_tCRTOpr.LineWidth * s_tCRTOpr.Yres);
    if (s_tCRTOpr.pDispMem == NULL)
    {
        DebugPrint(APP_ERR"s_tCRTOpr.pDispMem malloc err!\n");
        return -1;
    }
        
    return 0;
}

/*!
 * @brief  "framebuffer显示设备"的初始化函数
 * @param  [in] 无
 * @return 0：注册成功，其他值：失败
 */
static int CRTDeviceExit(void)
{
    /* 释放内存 */
	free(s_tCRTOpr.pDispMem);

    /* 清屏 */
    gl_clearscreen(0);

    /* 设置为文本模式 */
	vga_setmode(TEXT);
	return 0;
}

/*!
 * @brief  "framebuffer显示设备"的初始化函数
 * @param  [in] 无
 * @return 0：注册成功，其他值：失败
 */
static int CRTShowPixel(int x, int y, unsigned int color)
{
	int red, green, blue;
	
	red   = (color >> 16) & 0xff;
	green = (color >> 8) & 0xff;
	blue  = (color >> 0) & 0xff;

    /* 设置像素的RGB到virtualscreen(虚拟显存) */
    gl_setpixelrgb(x, y, red, green, blue);

    /* 像素信息复制到physicalscreen(物理显存)上 */
    gl_copyscreen(s_physicalscreen);
	return 0;
}

/*!
 * @brief  "framebuffer显示设备"的初始化函数
 * @param  [in] 无
 * @return 0：注册成功，其他值：失败
 */
static int CRTCleanScreen(unsigned int backcolor)
{
	int x;
	int y;
	int red, green, blue;
	
	red   = (backcolor >> 16) & 0xff;
	green = (backcolor >> 8) & 0xff;
	blue  = (backcolor >> 0) & 0xff;
    
	for (x = 0; x < 320; x++)
		for (y = 0; y < 200; y++)
            gl_setpixelrgb(x, y, red, green, blue);

    gl_copyscreen(s_physicalscreen);
        
	return 0;		
}

/*!
 * @brief  把ptVideoMem中的颜色数据在显示器上显示出来
 * @param  pixel_datas[in] 内含整屏的象素数据
 * @return 0：成功，-1：失败
 */
static int CRTShowPage(PT_PIXELDATAS pixel_datas)
{
    int x, y;
    unsigned int *color_src = (unsigned int *)pixel_datas->PixelDatas;
    unsigned int color;
    unsigned int red, green, blue;
    
    if (pixel_datas->bpp != 32)
    {
        DebugPrint(APP_ERR"Can not support %d \n", pixel_datas->bpp);
        return -1;
    }
    
    /*!
     * 逐行处理颜色数据
     */
    for (y = 0; y < s_tCRTOpr.Yres; y++)
    {
        /* 逐个处理 */
        for (x = 0; x < s_tCRTOpr.Xres; x++)
        {
            /* 0x00RRGGBB */
            color   = *color_src++;
            red   = (color >> 16) & 0xff;
            green = (color >> 8)  & 0xff;
            blue  = (color >> 0)  & 0xff;
            
            /* 设置像素的RGB到virtualscreen(虚拟显存) */
            gl_setpixelrgb(x, y, red, green, blue);
        }
    }

    /*!
     * 像素信息复制到physicalscreen(物理显存)上
     */
    gl_copyscreen(s_physicalscreen);
    return 0;
}

/*!
 * @brief  "framebuffer显示设备"的初始化函数
 * @param  [in] 无
 * @return 0：注册成功，其他值：失败
 */
int CRTInit(void)
{
	return RegisterDispOpr(&s_tCRTOpr);
}

